<?php

/**
 * 花之冠菜鸟仓库对接
 * @author chenshubo
 */

namespace App\Service\VendorClient\ShopCaiNiao;

use App\Service\BaseService;
use App\Service\Code\Normal;
use Service\AppService\IdService;
use App\Service\TaskServer\TaskWorker;
use App\Model\PssModel\ServiceConfig;
use Hyperf\Utils\WaitGroup;

class ShopCaiNiaoApiClient extends BaseService
{
    protected $clientConfig;
    public $routers=[
        "orderCreate"=>"CROSSBORDER_ORDER_CONSIGN", 
        "orderCancel"=>"CROSSBORDER_SALES_CANCEL",
        "orderExpress"=>"CROSSBORDER_LOGISTICS_DETAIL_QUERY",
        "goodsQueryList"=>"GIC_INVENTORY_QUERY",
        "goodsQueryStock"=>"ERP_SKU_GET",
        "goodsQueryCustoms"=>"ERP_QUERY_CUSTOMS_RECORD"
    ];

    public function __construct( $apiBody = [] )
    {
        $this->clientConfig = $apiBody;
    }
    
    /**
     * 创建订单
     */
    public function createOrder($param = [])
    {
        $orderInfo = $param['supplierOrder'];
        $orderItem = $param['orderGoods'];
        $order = $param['order'];
        $binfo = $param['binfo'];
        //支付流水号
        $pay_sn = $order['paySerial'] ?? $orderInfo['pay_sn'];
        //运费
        $deliver_money = bcmul($orderInfo['deliver_money'], 100);
        //支付时间
        $pay_time = date('Y-m-d H:i:s', $orderInfo['pay_time']);
        $order_sn = $orderInfo['supplier_dsn'];

        //组装商品数据列表
        $goodsList = [];
        $goodsTotalMoney = $ConsumptionTax = $CustomsTax = $VAT = $TotalTax = $Insurance = $Coupon = 0;
        foreach ($orderItem as $item) 
        {
            $tax_rate   = bcadd('1', (string)$item['tax_rate'], 4);
            $price      = $item['price'];
            $spec_num   = $item['spec_num'];
            $quantity   = $item['quantity'];

            //不含税价格
            $goodsPrice     = bcdiv((string)$price, (string)$tax_rate, 3);
            $goodsPrice     = ceil(bcmul((string)$goodsPrice, (string)100)) / 100;
            $buyNum         = $spec_num * $quantity;
            $goodsTotalPrice = bcmul((string)$goodsPrice, (string)$quantity, 2);
            $goodsTotalPrice = bcmul((string)$goodsTotalPrice, (string)100);
            //商品关税当前币种最小单位（分）
            $goodsCustomsTax = bcsub((string)$price, (string)$goodsPrice, 2);
            $goodsCustomsTax = bcmul((string)$goodsCustomsTax, (string)100);
            //商品消费税
            $goodsConsumptionTax = 0;
            //商品增值税
            $goodsVAT = 0;
            //商品综合税,当前币种最小单位（分）
            $goodsCustomsTax = bcmul((string)$goodsCustomsTax, (string)$quantity);
            $goodsList[] = [
                'itemId'                => $item['supplier_skucode'],
                'goodsQuantity'         => $buyNum,
                'goodsTotalPrice'       => (int)$goodsTotalPrice,//该明细行商品总价（优惠前）当前币种最小单位（分）(商品完税价)
                'goodsTotalActualPrice' => (int)$goodsTotalPrice,//该明细行商品总价（优惠后）当前币种最小单位（分）（商品实付金额）
                'goodsCustomsTax'       => (int)$goodsCustomsTax,//商品关税
                'goodsConsumptionTax'   => (int)$goodsConsumptionTax,//商品消费税
                'goodsVAT'              => (int)$goodsVAT,//商品增值税
                'goodsTotalTax'         => (int)$goodsCustomsTax,//商品综合税,当前币种最小单位（分）
                'hscode'                => $item['declare_hscode'],//hscode
            ];
            $goodsTotalMoney    = bcadd((string)$goodsTotalMoney, (string)$goodsTotalPrice);
            $ConsumptionTax     = bcadd((string)$ConsumptionTax, (string)$goodsConsumptionTax);
            $VAT                = bcadd((string)$VAT, (string)$goodsVAT);
            $CustomsTax         = bcadd((string)$CustomsTax, (string)$goodsCustomsTax);
            $TotalTax           = bcadd((string)$TotalTax, (string)$goodsCustomsTax);
        }

        //收货地址信息
        $receiverContact = [
            'contactName'   => $orderInfo['receive_name'],
            'zipCode'       => $this->clientConfig['zipCode'] ?? 300300,
            'address'       => $orderInfo['receive_address'],
            'province'      => $orderInfo['province'],
            'city'          => $orderInfo['city'],
            'district'      => $orderInfo['district'],
            'mobilePhone'   => $orderInfo['recveive_phone'],
        ];
        
        //发货地址信息
        $senderContact = [
            'contactName'   => $this->clientConfig['senderContactContactName'],
            'zipCode'       => $this->clientConfig['senderContactZipCode'],
            'address'       => $this->clientConfig['senderContactAddress'],
            'country'       => 'CN',
            'province'      => $this->clientConfig['senderContactProvince'],
            'city'          => $this->clientConfig['senderContactCity'],
            'district'      => $this->clientConfig['senderContactDistrict'],
            'mobilePhone'   => $this->clientConfig['senderContactMobilePhone'],
        ];

        //退货地址信息
        $refunderContact = [
            'contactName'   => $this->clientConfig['refunderContactName'],
            'zipCode'       => $this->clientConfig['refunderContactZipCode'],
            'address'       => $this->clientConfig['refunderContactAddress'],
            'country'       => 'CN',
            'province'      => $this->clientConfig['refunderContactProvince'],
            'city'          => $this->clientConfig['refunderContactCity'],
            'district'      => $this->clientConfig['refunderContactDistrict'],
            'mobilePhone'   => $this->clientConfig['refunderContactMobilePhone'],
        ];

        //订单完税价，当前币种最小单位（分）注意：订单完税价＝订单商品｜优惠前｜价格总和＋运费＋保费
        $DutiablePrice = bcadd((string)$goodsTotalMoney, (string)$deliver_money);
        $DutiablePrice = bcadd((string)$DutiablePrice, (string)$Insurance);

        //实际支付金额 = $DutiablePrice + $TotalTax - $Coupon
        $Actualpayment = bcadd($DutiablePrice, $TotalTax);

        //订单买家实付金额,当前币种最小单位（分）
        $real_total_money = $orderInfo['real_total_money'];
        $real_total_money = bcmul((string)$real_total_money, (string)100);

        //优惠金额度
        $Coupon = bcsub((string)$Actualpayment, (string)$real_total_money);

        //支付电商平台
        $apply_extends = @json_decode($orderInfo['apply_extends'], true);
        $enterpriseNum = $apply_extends['enterpriseNum'] ?? '';
        $enterpriseNum = $order['enterpriseNum'] ? $order['enterpriseNum'] : $enterpriseNum;

        //获取电商平台映射配置
        $channel_value = ServiceConfig::instance()->getConfigByEventsValue('cainiao', ServiceConfig::TYPE_CHANNEL, $enterpriseNum);
        if (empty($channel_value)) {
            return Normal::returnCode(Normal::FAIL, "支付电商平台编码：【{$enterpriseNum}】未映射，请联系管理员处理");
        }
        
        //获取支付方式配置
        $payCommanyCode = $apply_extends['payCommanyCode'] ?? '';
        $PayCompanyCode = $order['payCommanyCode'] ? $order['payCommanyCode'] : $payCommanyCode;
        $pay_value = ServiceConfig::instance()->getConfigByEventsValue('cainiao', ServiceConfig::TYPE_PAY, $PayCompanyCode);
        if (empty($pay_value)) {
            return Normal::returnCode(Normal::FAIL, "菜鸟仓当前不支持支付方式：{$PayCompanyCode}");
        }

        //创建订单数据
        $data = [
            'externalOrderId'       => $order_sn,
            'userId'                => !empty($this->clientConfig['userId']) ? $this->clientConfig['userId'] : $this->clientConfig['ownerUserId'],
            'businessUnitId'        => !empty($this->clientConfig['ownerBuId']) ? $this->clientConfig['ownerBuId'] : $this->clientConfig['businessUnitId'],
            'shopNick'              => $binfo['name'],
            'orderSource'           => $channel_value,
            'orderType'             => '01',
            'storeCode'             => $this->clientConfig['storeCode'],//保税仓／海外仓仓库编码
            'postFee'               => (int)$deliver_money,//不包含在商品价格中的运杂费。当前币种最小单位（分）
            'currency'              => 'CNY',//币制
            'buyerName'             => $orderInfo['buyer_realname'],
            'buyerId'               => $binfo['bid'],
            'buyerIDType'           => 1,//1-身份证,2-其它。限定为身份证，填写“1”。如果支付宝实名制验证的证件类型不是身份证，则提示证件类型不是身份证，不允许申报。
            'buyerIDNo'             => $orderInfo['buyer_idcard'],//支付人人的身份证件号码，判断是否是15位或者18位
            'payType'               => $pay_value,//支付企业名称，支付宝支付 01;微信支付 02;
            'payOrderId'            => $pay_sn,//支付企业唯一的支付流水号。
            'buyTime'               => date("Y-m-d H:i:s", $orderInfo['cd']),//下单时间yyyy-MM-dd HH:mm:ss
            'payTime'               => date("Y-m-d H:i:s", time()),//支付时间yyyy-MM-dd HH:mm:ss
            'message'               => $orderInfo['order_remark'],//客户留言
            'DutiablePrice'         => (int)$DutiablePrice,//订单完税价，当前币种最小单位（分）注意：订单完税价＝订单商品｜优惠前｜价格总和＋运费＋保费
            'CustomsTax'            => (int)$CustomsTax,//订单关税，当前币种最小单位（分）
            'ConsumptionTax'        => (int)$ConsumptionTax,//订单消费税最小单位（分）
            'VAT'                   => (int)$VAT,//订单消费税,当前币种最小单位（分）
            'TotalTax'              => (int)$TotalTax,//订单增值税,当前币种最小单位（分）
            'Insurance'             => (int)$Insurance,//订单保险费,当前币种最小单位（分）
            'Coupon'                => (int)$Coupon,//订单优惠,当前币种最小单位（分）
            'Actualpayment'         => (int)$real_total_money,//订单买家实付金额,当前币种最小单位（分）
            'goodsList'             => $goodsList,
            'receiverContact'       => $receiverContact,
            'senderContact'         => $senderContact,
            'refunderContact'       => $refunderContact,
        ];
        
        //指定菜鸟库存分组发货（余外特殊需求）
        if( !empty($this->clientConfig['invAllocation']) ) {
            $extendsField = [
                'invAllocation' => (string)$this->clientConfig['invAllocation']
            ];
            $data['extendsField'] = json_encode($extendsField,JSON_UNESCAPED_UNICODE);
        }
        
        $requestParam = [
            'method'    => 'orderCreate',//CROSSBORDER_ORDER_CONSIGN
            'data'      => $data,
            'format'    =>'json'
        ];
        $res = $this->execute($requestParam);
        if ($res['code'] == Normal::SUC) {
            return Normal::returnCode(Normal::SUC, "订单推送成功", [
                'thirdpart_order_sn' => $res['data']['lgorderCode']
            ]);
        }
        return $res;
    }

    /**
     * 取消订单
     */
    public function cancelOrder($params = [])
    {
        if (empty($params['supplierOrder'])) {
            return Normal::returnCode(Normal::FAIL, "except param supplierOrder");
        }

        if (empty($params['orderGoods'])) {
            return Normal::returnCode(Normal::FAIL, "except param orderGoods");
        }

        $data = [
            'userId'        => $this->clientConfig['ownerUserId'],
            'lgOrderCode'   => $params['supplierOrder']['thirdpart_order_sn']
        ];
        $requestParam = [
            'method'    => 'orderCancel', //CROSSBORDER_SALES_CANCEL
            'data'      => $data,
            'format'    =>'json'
        ];
        return $this->execute($requestParam);
    }

    /**
     * 获取sign参数
     */
    private function getSign($param = [],$format='json')
    {
        $param=$format=='xml'?$this->toXml($param,true,'request'):json_encode($param);
        return base64_encode(md5($param . $this->clientConfig['appSecret'], true));
    }

    /**
     * 获取商品列表
     */
    public function queryGoodsList($param=[])
    {
        $data = [
            'ownerUserId'    => $this->clientConfig['ownerUserId'],
            'pageNo'         => $param['page']  ?? 1,
            'pageSize'       => $param['limit'] ?? 50,
            "businessUnitId" => $this->clientConfig['businessUnitId'],
            'storeCode'      => $this->clientConfig['storeCode'],
        ];
        $requestParam = [
            'method' => 'goodsQueryList',//GIC_INVENTORY_QUERY
//            'method' => 'GIC_BATCH_INVENTORY_QUERY',//goodsQueryList
            'data' => $data,
            'format'=>'xml'
        ];
        $responseParam = $this->execute($requestParam);
        if ($responseParam['code']!=Normal::SUC){
            return $responseParam;
        }

        if( empty($responseParam['data']['itemList']['item']) || (isset($this->clientConfig['type']) && $this->clientConfig['type']==1)) {
            return $responseParam;
        }
        if (count($responseParam['data']['itemList']['item']) == count($responseParam['data']['itemList']['item'], 1)){
            $skuNos = [$responseParam['data']['itemList']['item']['itemId']];
            $responseParam['data']['itemList']['item']=[$responseParam['data']['itemList']['item']];
        }else{
            $skuNos = array_column($responseParam['data']['itemList']['item'],'itemId');
        }
        $goodsDetail=$this->queryGoodsDetail('goodsQueryStock',$skuNos);//ERP_SKU_GET
        $goodsCustomRecord=$this->queryGoodsDetail('goodsQueryCustoms',$skuNos);//ERP_QUERY_CUSTOMS_RECORD
//        $goodsBatches=$this->queryGoodsDetail('goodsQueryBatch',$skuNos);//ERP_BATCH_INV_QUERY
        foreach ($responseParam['data']['itemList']['item'] as &$v){
            if (isset($goodsDetail[$v['itemId']])){
                $v=array_merge($v,$goodsDetail[$v['itemId']]);
            }
            if (isset($goodsCustomRecord[$v['itemId']])){
                $v=array_merge($v,$goodsCustomRecord[$v['itemId']]);
            }
//            if (isset($goodsBatches[$v['itemId']])){
//                $v['batch']=$goodsBatches[$v['itemId']];
//            }
            unset($v);
        }
        return $responseParam;
    }

    /**
     * 查询商品详情
     */
    public function queryGoodsDetail($method,$skuNos)
    {
        $details=[];
        $count=count($skuNos);
        $wg = new WaitGroup();
        $wg->add($count);
        foreach ($skuNos as $sku){
            co(function () use ($wg,$method,$sku,& $details) {
                $data = [
                    'itemId'=> $sku
                ];
                if ($method=='goodsQueryCustoms'){
                    $data['userId']=$this->clientConfig['ownerUserId'];
                }elseif($method=='goodsQueryStock'){
                    $data['ownerUserId']=$this->clientConfig['ownerUserId'];
                }elseif ($method=='goodsQueryBatch'){
                    $data=[
                        'ownerCode'=>$this->clientConfig['ownerUserId'],
                        'warehouseCode'=>$this->clientConfig['storeCode'],
                        'page'=>1,
                        'pageSize'=>20,
                    ];
//                    $data['ownerCode']=$this->clientConfig['ownerUserId'];
//                    $data['warehouseCode']=$this->clientConfig['storeCode'];
//                    $data['page']=1;
//                    $data['pageSize']=20;
                }
                $requestParam = [
                    'method' => $method,
                    'data'   => $data,
                    'format' => 'xml'
                ];
                $responseParam = $this->execute($requestParam);
                if ($responseParam['code']==Normal::SUC && isset($responseParam['data']['itemId'])){
                    $details[$responseParam['data']['itemId']]=$responseParam['data'];
                }
                $wg->done();
            });
        }
        $wg->wait();
        return $details;
        
    }

    /**
     * CURL请求
     * @param $params
     * @return mixed
     */
    public function execute( $param = [] )
    {
        $method     = $param['method'];
        $data       = $param['data'];
        if( empty($this->clientConfig['baseurl']) ) {
            return Normal::returnCode(Normal::FAIL,"当前仓库参数baseurl未配置");
        }

        if( empty($this->routers[$method]) ) {
            return Normal::returnCode(Normal::FAIL,"当前仓库参数【{$method}】未配置");
        }
        
        $format   = $param['format']??'json';
        $data_digest = $this->getSign($param['data'],$format);
        $param = [
            'msg_type'              => $this->routers[$method],
            'logistic_provider_id'  => $this->clientConfig['cpCode'],
            'data_digest'           => $data_digest,
            'logistics_interface'   => $format == 'xml' ? $this->toXml($param['data'],true,'request') : json_encode($param['data'])
        ];
        $url = $this->clientConfig['baseurl'];
        $parameter = [
            [
                'tag'       => 'cainiao_' . $method,
                'uid'       => IdService::instance()->getOtherID(),
                'url'       => $url,
                'data'      => $param,
                'method'    => 'post',
                'format'    => 'x-www-form-urlencoded',
                'header'    => [],
                
            ],
        ];
        
        if( in_array($method,['goodsQueryList','goodsQueryStock','goodsQueryCustoms']) ) {
            $resTask = TaskWorker::instance()->addTask($parameter,'handleDiableTask');
        } else {
            $resTask = TaskWorker::instance()->addTask($parameter);
        }
        
        $resTask = current($resTask);
        $res = $resTask['response']['result'];
        $resHttp = json_decode($res, true);
        if (empty($resHttp)) {
            $resHttp = json_decode(json_encode(simplexml_load_string($res, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        }

        $errorMsg = $resHttp['errorMsg'] ?? '请求失败';
        if (isset($resHttp['success']) && $resHttp['success'] == 'true') {
            return Normal::returnCode(Normal::SUC, "操作成功", $resHttp);
        }
        return Normal::returnCode(Normal::FAIL, $errorMsg);
    }


    /**
     *   将数组转换为xml
     * @param array $data 要转换的数组
     * @param bool $root 是否要根节点
     * @param bool $rootName 根节点名称
     * @return string         xml字符串
     */
    function toXml($data, $root = true, $rootName = 'xml')
    {
        $str = "";
        if ($root) {
            $str .= "<{$rootName}>";
        }
        foreach ($data as $key => $val) {
            if (is_array($val)) {
                $child = $this->toXml($val, false);
                $str .= "<$key>$child</$key>";
            } else {
                $str .= "<$key>$val</$key>";
            }
        }
        if ($root) {
            $str .= "</{$rootName}>";
        }
        return $str;
    }
}